/*
 * BSM6.0
 *
 * Copyright 2012-2013 (C) SINODATA CO., LTD. All Rights Reserved.
 */
package com.sinodata.bsm.center.engine.resource.find;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.sinodata.bsm.center.cache.ResCache;
import com.sinodata.bsm.center.cache.ResFindDefCache;
import com.sinodata.bsm.center.cache.ResTypeCache;
import com.sinodata.bsm.center.message.Messenger;
import com.sinodata.bsm.center.util.spring.SpringContextHolder;
import com.sinodata.bsm.common.message.ServiceMessage;
import com.sinodata.bsm.common.utils.ArgumentUtil;
import com.sinodata.bsm.common.vo.Res;
import com.sinodata.bsm.common.vo.ResFindDef;
import com.sinodata.bsm.common.vo.ResType;

/**
 * <p>
 * Description:
 * </p>
 * 
 * @author tangli
 * @version 1.0
 * 
 * <p>
 * History:
 * 
 * Date Author Version Description
 * ---------------------------------------------------------------------------------
 * 2012-5-24 上午10:04:42 tangli 1.0 To create
 * </p>
 * 
 * @since
 * @see
 */
public final class ResourceFinder {
    private static Logger logger = Logger.getLogger(ResourceFinder.class);
    private static Pattern ATTR_PATTERN = Pattern.compile("\\$\\{(\\d+)\\}");
    private final ResCache resCache = SpringContextHolder.getBean(ResCache.class);
    private final ResTypeCache resTypeCache = SpringContextHolder.getBean(ResTypeCache.class);
    private final ResFindDefCache resFindDefCache = SpringContextHolder.getBean(ResFindDefCache.class);

    private ResourceFinder() {
    }

    public static void doFind(final Long resId) throws Exception {
        FindResGroup group = FindResFactory.getInstance().getResGroup(resId);
        if (!group.isFinish()) {
            logger.info("该资源已有其他线程在发现。");
            throw new Exception("资源已经在发现");
        }
        group.setFinish(false);
        group.build();
        new Thread() {
            @Override
            public void run() {
                try {
                    new ResourceFinder().find(resId);
                } catch (Exception e) {
                    logger.error("资源发现失败:" + resId, e);
                }
            }
        }.start();
    }

    private void find(Long resId) throws Exception {
        Res res = resCache.get(resId);
        FindResGroup group = FindResFactory.getInstance().getResGroup(resId);
        try {
            ResFindDef resFindDef = resFindDefCache.getForResType(res.getResTypeId());
            if (resFindDef == null) {
                List<ResFindDef> includes = resFindDefCache.getIncludeForResType(res.getResTypeId());
                for (ResFindDef def : includes) {
                    find(group, res.getProbeId(), res.getIp(), def, group.getParents(), def.getResTypeId());
                }
            } else {
                find(group, res.getProbeId(), res.getIp(), resFindDef, group.getParents());
            }
        } catch (Exception e) {
            logger.error(">>>> error execute find  method!", e);
        } finally {
            group.setFinish(true);
        }
    }

    /**
     * 发现根资源的属性
     * 
     * @param probeId
     * @param ip
     * @param finder
     * @param member
     * @throws Exception
     */
    private void find(FindResGroup group, Long probeId, String ip, ResFindDef finder, FindResBean member) throws Exception {
        group.getFindLogs().get(finder.getId()).setStatus(0);
        String className = null;
        Map<String, String> params = null;
        String str = finder.getCollector();
        if (str.contains(",")) {
            int idx = str.indexOf(",");
            className = str.substring(0, idx);
            params = ArgumentUtil.parseToMap(str.substring(idx + 1));
            prepareFindArgument(params, member);
        } else {
            className = str;
            params = new HashMap<String, String>();
        }
        List<String[]> findResult = execute(probeId, ip, className, params);
        if (findResult != null && findResult.size() == 1) {
            member.checkChange(findResult.get(0), params.get("ATTR"));
        }
        group.getFindLogs().get(finder.getId()).setStatus(1);
        List<ResFindDef> includes = resFindDefCache.getIncludeForResType(member.getRes().getResTypeId());
        for (ResFindDef def : includes) {
            find(group, probeId, ip, def, member, def.getResTypeId());
        }
    }

    /**
     * 根据资源类别发现子资源
     * 
     * @param probeId
     * @param ip
     * @param finder
     * @param member
     * @param resTypeId
     * @throws Exception
     */
    private void find(FindResGroup group, Long probeId, String ip, ResFindDef finder, FindResBean member, Long resTypeId) throws Exception {
        if (group.getFindLogs().get(finder.getId()) != null) {
            group.getFindLogs().get(finder.getId()).setStatus(0);
        }
        String className = null;
        Map<String, String> params = null;
        String str = finder.getCollector();
        if (str.contains(",")) {
            int idx = str.indexOf(",");
            className = str.substring(0, idx);
            params = ArgumentUtil.parseToMap(str.substring(idx + 1));
            prepareFindArgument(params, member);
        } else {
            className = str;
            params = new HashMap<String, String>();
        }
        List<String[]> findResult = execute(probeId, ip, className, params);
        if (findResult != null) {
            member.checkChange(findResult, params.get("ATTR"), resTypeId);
        }
        for (FindResBean child : member.getChildren()) {
            if (child.getChangeType() != FindResBean.CHANGE_DEL) {
                Long typeId = child.getRes().getResTypeId();
                List<ResFindDef> includes = resFindDefCache.getIncludeForResType(typeId);
                for (ResFindDef def : includes) {
                    find(group, probeId, ip, def, child, def.getResTypeId());
                }
                // ResFindDef d = resFindDefCache.getForResType(typeId);
                // if (d != null) {
                // find(probeId, ip, d, child, typeId);
                // }
            }
        }
        if (group.getFindLogs().get(finder.getId()) != null) {
            group.getFindLogs().get(finder.getId()).setStatus(1);
        }
    }

    private List<String[]> execute(Long probeId, String ip, String className, Map<String, String> params) throws Exception {
        ServiceMessage message = new ServiceMessage();
        message.setSrvId("ResFindService");
        message.setMethod("find");
        message.setParams(new Object[] { ip, params, className });
        logger.info("send find res request:probeId=" + probeId + ",message=" + message);
        return (List<String[]>) Messenger.getInstance().sendSyncMessage(probeId, message);
    }

    /**
     * 准备发现的参数，参数中对属性的使用，采用${attrid}变量
     * 
     * @param params
     * @param parent
     */
    private void prepareFindArgument(Map<String, String> params, FindResBean member) {
        for (String key : params.keySet()) {
            String pv = params.get(key);
            if (pv != null) {
                while (pv.matches(".*\\$\\{(\\d+)\\}.*")) {
                    Matcher matcher = ATTR_PATTERN.matcher(pv);
                    String attrIdStr = null;
                    if (matcher.find()) {
                        attrIdStr = matcher.group(1);
                    }
                    Long attrId = Long.parseLong(attrIdStr);
                    String val = member.attributeValue(attrId);
                    if (val == null) {
                        logger.error("资源发现的参数不合法：" + params.get(key));
                        break;
                    } else {
                        pv = pv.replaceAll("\\$\\{" + attrId + "\\}", val);
                    }

                }
                if (pv.indexOf("$IID") != -1) {
                    String iid = member.getRes().getInternalId();
                    pv = pv.replaceAll("\\$IID", iid != null ? iid : "");
                }
                params.put(key, pv);
            }
        }
    }
}
